﻿using System;
using System.Linq;
using System.Runtime.Serialization;

using Game.Common;
using Game.Common.Visitor;
using Game.ExtensibleObjects;
using Game.Objects;

namespace Game.Map.Extensions
{
    public static class VehicleExtensionHelper
    {
        public static VehicleExtension VehicleX(this IMapField field)
        {
            return field.Extensions.Find<VehicleExtension>();
        }

        public static IMapField FindItem(
            this IGameMap map,
            Vehicle find)
        {
            return map.Fields.FirstOrDefault(field => find == field.VehicleX().Vehicle);
        }
    }

    [DataContract(Namespace = "game")]
    public class VehicleExtension : IRuntimeExtension, IAttachable, IVisitorElement
    {
        public event EventHandler<FieldVehicleEventArgs> VehicleAssigned;
        public event EventHandler<FieldVehicleEventArgs> VehicleChanged;

        IMapField _Field;

        [DataMember]
        public Vehicle _Vehicle;

        public VehicleExtension(IRuntimeExtensibleObject obj)
        {
            _Field = obj as IMapField;
        }

        public Vehicle Vehicle
        {
            get { return _Vehicle; }
            set
            {
                if (_Vehicle == value) return;

                if (_Vehicle != null)
                {
                    _Vehicle.PlayerChanged -= OnPlayerChanged;
                    _Vehicle.Changed -= OnVehicleChanged;
                }

                var oldValue = _Vehicle;
                _Vehicle = value;

                if (_Vehicle != null)
                {
                    _Vehicle.PlayerChanged += OnPlayerChanged;
                    _Vehicle.Changed += OnVehicleChanged;
                }

                FireVehicleAssigned(oldValue);
            }
        }

        public void Attach(IRuntimeExtensibleObject field)
        {
            _Field = field as IMapField;
        }

        [OnDeserialized]
        public void OnDeserialized2(StreamingContext c)
        {
            if (_Vehicle != null)
            {
                _Vehicle.PlayerChanged += OnPlayerChanged;
                _Vehicle.Changed += OnVehicleChanged;
            }
        }

        void FireVehicleAssigned(Vehicle oldValue)
        {
            if (VehicleAssigned != null)
            {
                var args = new FieldVehicleEventArgs(_Field, oldValue);
                VehicleAssigned(this, args);
            }
        }

        void FireVehicleChanged(Vehicle oldValue)
        {
            if (VehicleChanged != null)
            {
                var args = new FieldVehicleEventArgs(_Field, oldValue);
                VehicleChanged(this, args);
            }
        }
        
        void OnPlayerChanged(object sender, HasOwnerEventArgs e)
        {
            FireVehicleAssigned(_Vehicle);
        }

        void OnVehicleChanged(object sender, ContainerEventArgs e)
        {
            FireVehicleChanged(_Vehicle);
        }

        public void Accept(IVisitor visitor)
        {
            if (_Vehicle != null)
            {
                _Vehicle.Accept(visitor);
            }
        }
    }

    public class VehicleExtensionFactory : IRuntimeExtensionFactory
    {
        public IRuntimeExtension CreateExtension(IRuntimeExtensibleObject source)
        {
            return new VehicleExtension(source);
        }
    }

    public class FieldVehicleEventArgs : EventArgs
    {
        readonly IMapField _mapField;
        readonly Vehicle _oldItem;

        public FieldVehicleEventArgs(IMapField mapField, Vehicle oldItem)
        {
            _mapField = mapField;
            _oldItem = oldItem;
        }

        public IMapField MapField
        {
            get { return _mapField; }
        }

        public Vehicle OldItem
        {
            get { return _oldItem; }
        }
    }

}
